`include "timescale.v"

module ahb_ctrl(
	input 		   hresetn,
	input 		   hclk,
	
	output reg [31:0]  haddr,
	output reg [1:0]   htrans,
	output reg 		   hwrite,
	output reg [2:0]   hsize,
	output reg [2:0]   hburst,
	output reg [3:0]   hprot,
	output reg [31:0]  hwdata,

	// output 		   hsel,
	input  		   hready,
	input  [1:0]   hresp,
	input  [31:0]  hrdata, 

	input  [31:0]  tx_bd_pointer,		//the apb register's output 
	input 		   LatchWB,
	input 		   LatchTx		//the start signal
);

// reg 			  start;
reg 	  [31:0]  TxBDConfig, TxBDAddr;
wire 			  StartIdle, StartReqBus, StartReadBD, StartReadFrame, StartReadNextBD, StartWriteBack;
reg 			  StateIdle, StateReqBus, StateReadBD, StateReadFrame, StateReadNextBD, StateWriteBack;

// fields of TxBDConfig reg
// wire 	  [10:0]  TxFrameLen;
// assign 			  TxFrameLen  = TxBDConfig[10:0];
`define TxBD_UE [14]
`define TxBD_IE [13]
`define TxBD_WR [12]
`define TxBD_EN [11]
`define TxBD_Length [10:0]
//usage : TxBDConfig`TxBD_Length, TxBDConfig`TxBD_EN


// ==================== counter ====================
reg 	  [10:0]  WordCnt;				//max 2048
wire 			  ResetWordCnt, IncrementWordCnt;
assign 			  ResetWordCnt = StartReadBD | StartReadFrame | StartReadNextBD;
assign 			  IncrementWordCnt = ~ResetWordCnt & (StateReadBD | StateReadFrame | StateReadNextBD) ;
always@(posedge hclk or negedge hresetn)
begin
  if(!hresetn)
	WordCnt <= 11'd0;
  else if(ResetWordCnt)
	WordCnt <= 11'd0;
  else if(IncrementWordCnt)
	WordCnt <= WordCnt + 11'd1;
end

wire 			  WordCntEq2;
assign 			  WordCntEq2  = WordCnt[1:0] == 2'd2;

wire 			  WordCntEq0;
assign 			  WordCntEq0  = WordCnt[1:0] == 2'd0;

wire 			  ReadFrameFinish;
// assign 			  ReadFrameFinish = WordCnt == {TxBDConfig`TxBD_Length}[10:2];
assign 			  ReadFrameFinish = {WordCnt, 2'b00} == TxBDConfig`TxBD_Length;

wire 			  ReadFrameAlmostFinish;
assign 			  ReadFrameAlmostFinish = {WordCnt+11'd1, 2'b00} == TxBDConfig`TxBD_Length;


// -------------------- DMA ctrl fsm --------------------
// assign 			  StartIdle   = StateReadFrame & ReadFrameFinish;
// assign 			  StartIdle   = StateReadFrame & ReadFrameAlmostFinish;
assign 			  StartIdle   = StateReadNextBD & WordCntEq0 | StateWriteBack;
assign 			  StartReqBus = StateIdle & LatchTx;
assign 			  StartReadBD = StateReqBus & 1;
assign 			  StartReadFrame = StateReadBD & WordCntEq2;
assign 			  StartReadNextBD = StateReadFrame & ReadFrameAlmostFinish;

assign 			  StartWriteBack = StateIdle & LatchWB;



always@(posedge hclk or negedge hresetn)
begin
  if(!hresetn) begin
	StateIdle <= 1'b1;
	StateReqBus <= 1'b0;
	StateReadBD <= 1'b0;
	StateReadFrame <= 1'b0;
  end
  else begin
	if(StartReqBus)
	  StateIdle <= 1'b0;
	else if(StartIdle)
	begin
	  StateIdle <= 1'b1;
	end

	if(StartReadBD)
	  StateReqBus <= 1'b0;
	else if(StartReqBus)
	begin
	  StateReqBus <= 1'b1;
	end

	if(StartReadFrame)
	  StateReadBD <= 1'b0;
	else if(StartReadBD)
	begin
	  StateReadBD <= 1'b1;
	end

	if(StartReadNextBD)
	  StateReadFrame <= 1'b0;
	else if(StartReadFrame)
	  StateReadFrame <= 1'b1;

	if(StartIdle)
	  StateReadNextBD <= 1'b0;
	else if(StartReadNextBD)
	  StateReadNextBD <= 1'b1;


	if(StartIdle)
	  StateWriteBack <= 1'b0;
	else if(StartWriteBack)
	  StateWriteBack <= 1'b1;

  end
end
//////////////////// end of DMA ctrl fsm ////////////////////


////////////////////////////////////////

always@(posedge hclk)
begin
  if(StartWriteBack)
  begin
	haddr <= 32'h12345678;
	htrans <= 2'b10;
	hwrite <= 1'b0;
	hsize <= 3'b010;	//word
	hburst <= 3'b000;
	hprot <= 4'b0000;
	hwdata <= 32'habcdef00;
  end
  else if(StateWriteBack)
  begin
	htrans <= 2'b00;
  end
  else
  
  if(StartReadBD)
  begin
	//now start reading data
	haddr <= {tx_bd_pointer[31:10], tx_bd_pointer[9:3], 3'b000};
	htrans <= 2'b10;
	hwrite <= 1'b0;
	hsize <= 3'b010;	//word
	hburst <= 3'b000;
	hprot <= 4'b0000;
	hwdata <= 32'hx;
  end
  else if(StateReadBD)
  begin
	if(WordCnt == 0) begin
	  // haddr <= {tx_bd_pointer[31:10], tx_bd_pointer[9:3]+7'd1, 3'b000};	//or set the hburst as INCR ??
	  haddr <= {tx_bd_pointer[31:10], tx_bd_pointer[9:3], 3'b100};	//addr + 4
	end
	else if(WordCnt == 1) begin
	  TxBDConfig <= hrdata;
	  if(hrdata`TxBD_EN==1'b0)				//if BD not enabled, turn to idle
		$display("BD not enabled !");
	  //insert an idle clk ???
	  htrans <= 2'b00;			//insert a bus IDLE, the RAM should ignore this transmission
	end
	else if(WordCnt == 2) begin
	  TxBDAddr <= hrdata + 32'd4;
	  htrans <= 2'b10;			//transmit normally
	  //start read the data from buffer
	  haddr <= hrdata;
	end
  end 
  // else if(StateReadFrame)
  // begin
  // 	begin
  // 	  if(ReadFrameAlmostFinish) begin
  // 		htrans <= 2'b00;
  // 	  end
  // 	  // else if(!ReadFrameFinish) begin
  // 	  else begin
  // 		haddr <= TxBDAddr;
  // 		TxBDAddr <= TxBDAddr + 32'd4;
  // 	  end
  // 	end
  // end
  else if(StateReadFrame)
  begin
  	begin
  	  if(ReadFrameAlmostFinish) begin
  		// htrans <= 2'b00;
		hsize <= 3'b010;	//word
		// TxBDConfig`TxBD_Length
		haddr <= {tx_bd_pointer[31:10], tx_bd_pointer[9:3]+7'd1, 3'b100};
  	  end
  	  else begin
  		haddr <= TxBDAddr;
  		TxBDAddr <= TxBDAddr + 32'd4;
  	  end
  	end
  end

end




endmodule // ahb_ctrl

